package dao

import (
	"context"
	"errors"
	"time"

	"bilinadmin/internal/object"

	"bilinadmin/internal/model"
	"bilinadmin/pkg/db/pgex"
)

// EmployeeDao employee dao
type EmployeeDao interface {
	// CreateEmployee 创建员工
	CreateEmployee(ctx context.Context, emp *model.Employee) (int64, error)
	// UpdateEmployeeById 更新员工信息
	UpdateEmployeeById(ctx context.Context, emp *model.Employee) error
	// GetEmployeeRelatedTo 获取员工by account
	GetEmployeeRelatedTo(ctx context.Context, accountId string) ([]*model.Employee, error)
	// GetEmpByBrandAndAccount 按统一账号id，品牌获取员工
	GetEmpByBrandAndAccount(ctx context.Context, brandId int64, accountId string) (*model.Employee, error)
	// GetEmpById 获取员工信息
	GetEmpById(ctx context.Context, brandId, empId int64) (*model.Employee, error)
	// LockEmpById 锁定员工信息
	LockEmpById(ctx context.Context, brandId, empId int64) (*model.Employee, error)
	// CacheEmpVisitToken 缓存临时用户访问信息
	CacheEmpVisitToken(ctx context.Context, token string, info *object.EmpVisitInfo, exp time.Duration) error
	// GetEmpVisitToken 获取临时用户访问信息
	GetEmpVisitToken(ctx context.Context, token string) (*object.EmpVisitInfo, error)
}

var empDao EmployeeDao

type empDaoImpl struct {
	*DaoBase
	name string
}

// InitEmployeeDao 初始化 dao
func InitEmployeeDao() {
	empDao = &empDaoImpl{
		DaoBase: daoBase,
		name:    "employee",
	}
}

// GetEmployeeDao 获取 dao
func GetEmployeeDao() EmployeeDao {
	return empDao
}

// CreateEmployee 创建员工
func (dao *empDaoImpl) CreateEmployee(ctx context.Context, emp *model.Employee) (int64, error) {
	if emp == nil {
		return 0, nil
	}
	param := []interface{}{
		emp.BrandId,
		emp.Name,
		emp.Mobile,
		emp.Email,
		emp.EntryDate,
		emp.AccountId,
		emp.Status,
		emp.CreatedAt,
		emp.UpdatedAt,
	}
	query := "INSERT INTO employee (brand_id, name, mobile, email, entry_date, account_id, status, created_at, updated_at) " +
		"VALUES (?,?,?,?,?,?,?,?,?)"
	aff, err := dao.db.PgExec(ctx, "id", query, param...)
	if err != nil {
		return 0, err
	}
	lastId, err := aff.GetLastId()
	if err != nil {
		return 0, err
	}
	return lastId, nil
}

// UpdateEmployeeById 更新员工信息
func (dao *empDaoImpl) UpdateEmployeeById(ctx context.Context, emp *model.Employee) error {
	if emp == nil {
		return nil
	}
	param := []interface{}{
		emp.Name,
		emp.EntryDate,
		emp.AccountId,
		emp.Status,
		emp.UpdatedAt,
		emp.Id,
	}
	query := "UPDATE employee SET name=?, entry_date=?, account_id=?, status=?, updated_at=? WHERE id=? AND deleted_at=0"
	_, err := dao.db.Update(ctx, query, param...)
	return err
}

// GetEmployeeRelatedTo 获取员工by account
func (dao *empDaoImpl) GetEmployeeRelatedTo(ctx context.Context, accountId string) ([]*model.Employee, error) {
	var emps []*model.Employee
	query := "SELECT * FROM employee WHERE account_id=? AND deleted_at=0"
	err := dao.db.Query(ctx, &emps, query, accountId)
	return emps, err
}

// GetEmpByBrandAndAccount 按统一账号id，品牌获取员工
func (dao *empDaoImpl) GetEmpByBrandAndAccount(ctx context.Context, brandId int64, accountId string) (*model.Employee, error) {
	query := "SELECT * FROM employee WHERE brand_id=? AND account_id=? AND deleted_at=0"
	emp := &model.Employee{}
	err := dao.db.Get(ctx, emp, query, brandId, accountId)
	if errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return emp, err
}

// GetEmpById 获取员工信息
func (dao *empDaoImpl) GetEmpById(ctx context.Context, brandId, empId int64) (*model.Employee, error) {
	query := "SELECT * FROM employee WHERE brand_id=? AND id=? AND deleted_at=0"
	emp := &model.Employee{}
	err := dao.db.Get(ctx, emp, query, brandId, empId)
	if errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return emp, err
}

// LockEmpById 锁定员工信息
func (dao *empDaoImpl) LockEmpById(ctx context.Context, brandId, empId int64) (*model.Employee, error) {
	if err := dao.needTrans(ctx); err != nil {
		return nil, err
	}
	query := "SELECT * FROM employee WHERE brand_id=? AND id=? AND deleted_at=0 FOR UPDATE"
	emp := &model.Employee{}
	err := dao.db.Get(ctx, emp, query, brandId, empId)
	if errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return emp, err
}

// CacheEmpVisitToken 缓存临时用户信息访问token
func (dao *empDaoImpl) CacheEmpVisitToken(ctx context.Context, token string, info *object.EmpVisitInfo, exp time.Duration) error {
	key := dao.BuildCacheKey("visit_token", token)
	infoB, err := json.Marshal(info)
	if err != nil {
		return err
	}
	_, err = dao.cache.Set(ctx, key, infoB, exp)
	return err
}

// GetEmpVisitToken 获取临时用户访问信息
func (dao *empDaoImpl) GetEmpVisitToken(ctx context.Context, token string) (*object.EmpVisitInfo, error) {
	key := dao.BuildCacheKey("visit_token", token)
	res, err := dao.cache.Get(ctx, key)
	if err != nil && isNil(err) {
		return nil, nil
	}
	info := &object.EmpVisitInfo{}
	err = json.Unmarshal([]byte(res), info)
	return info, err
}
